#include "m_curl.h"
#include "segment.h"
#include "span.h"
#include "common.h"
#include "agent.h"
#include "helper.h"
#include "utils.h"
#include "scaler.h"
#include "pushgateway_client.h"
// #include "skywalking_client.h"

#include <map>
#include <vector>
#include <string>
#include <curl/curl.h>

#include "ext/standard/url.h"

// int is_record = 0;
std::map<long, std::map<std::string, Scaler *>> msc;

void init_scaler_map(long addr)
{
    if (msc.count(addr) <= 0)
    {
        Scaler *total = new Scaler;
        Scaler *second = new Scaler;
        msc[addr]["total"] = total;
        msc[addr]["second"] = second;
    }
}

void monitorpro_curl_setopt_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    zval *zid, *zvalue;
    zend_long options;
    std::string method = "GET";
    std::string item_name = ITEM_HTTP_METHOD;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "rlz", &zid, &options, &zvalue) == FAILURE)
    {
        return;
    }

    if (MONITORPRO_CURLOPT_POST == options)
    {
        switch (Z_TYPE_P(zvalue))
        {
        case IS_TRUE:
            method = VALUE_HTTP_METHOD_POST;
            break;
        case IS_FALSE:
            method = VALUE_HTTP_METHOD_GET;
            break;
        case IS_STRING:
            convert_to_long(zvalue);
        case IS_LONG:
            method = Z_LVAL_P(zvalue) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
            break;
        default:
            method = VALUE_HTTP_METHOD_GET;
            break;
        }
    }
    else
    {
        method = VALUE_HTTP_METHOD_GET;
    }

    // 获取函数传参
    if (MONITORPRO_G(pushenable))
    {
        long addr = reinterpret_cast<long>(zid);
        init_scaler_map(addr);
        msc[addr]["total"]->addItem(item_name, method);
        msc[addr]["second"]->addItem(item_name, method);
    }

    // if (CURLOPT_HTTPHEADER == options && Z_TYPE_P(zvalue) == IS_ARRAY)
    // {
    //     zval dup_header;
    //     ZVAL_DUP(&dup_header, zvalue);
    //     add_index_zval(&MONITORPRO_G(curl_header), Z_RES_HANDLE_P(zid), &dup_header);
    // }
}

void monitorpro_curl_setopt_array_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    zval *zid, *arr;
    std::string method;
    std::string item_name = ITEM_HTTP_METHOD;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "ra", &zid, &arr) == FAILURE)
    {
        return;
    }

    zval *zvalue = zend_hash_index_find(Z_ARRVAL_P(arr), MONITORPRO_CURLOPT_POST);
    if (zvalue != nullptr)
    {
        switch (Z_TYPE_P(zvalue))
        {
        case IS_TRUE:
            method = VALUE_HTTP_METHOD_POST;
            break;
        case IS_FALSE:
            method = VALUE_HTTP_METHOD_GET;
            break;
        case IS_STRING:
            convert_to_long(zvalue);
        case IS_LONG:
            method = Z_LVAL_P(zvalue) ? VALUE_HTTP_METHOD_POST : VALUE_HTTP_METHOD_GET;
            break;
        default:
            method = VALUE_HTTP_METHOD_GET;
            break;
        }
    }
    else
    {
        method = VALUE_HTTP_METHOD_GET;
    }

    // 获取函数传参
    if (MONITORPRO_G(pushenable))
    {
        long addr = reinterpret_cast<long>(zid);
        init_scaler_map(addr);
        msc[addr]["total"]->addItem(item_name, method);
        msc[addr]["second"]->addItem(item_name, method);
    }
    // zval *http_header = zend_hash_index_find(Z_ARRVAL_P(arr), CURLOPT_HTTPHEADER);
    // if (http_header != nullptr)
    // {
    //     zval copy_header;
    //     ZVAL_DUP(&copy_header, http_header);
    //     add_index_zval(&MONITORPRO_G(curl_header), Z_RES_HANDLE_P(zid), &copy_header);
    // }
}

void monitorpro_pre_curl_exec_handler(zend_execute_data *execute_data, zval *return_value, std::string class_name, std::string function_name)
{
    //     zval *zid;
    //     if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zid) == FAILURE)
    //     {
    //         return;
    //     }

    //     zval func;
    //     zval args[1];
    //     zval url_info;
    //     ZVAL_COPY(&args[0], zid);
    //     ZVAL_STRING(&func, "curl_getinfo");
    //     call_user_function(CG(function_table), nullptr, &func, &url_info, 1, args);
    //     zval_dtor(&func);
    //     zval_dtor(&args[0]);

    //     // check
    //     php_url *url_parse = nullptr;
    //     zval *z_url = zend_hash_str_find(Z_ARRVAL(url_info), ZEND_STRL("url"));
    //     char *url_str = Z_STRVAL_P(z_url);
    //     if (strlen(url_str) > 0 && (starts_with("http://", url_str) || starts_with("https://", url_str)))
    //     {
    //         url_parse = php_url_parse(url_str);
    //         if (url_parse->scheme != nullptr && url_parse->host != nullptr)
    //         {
    //             is_record = 1;
    //         }
    //     }

    //     // set header
    //     int is_emalloc = 0;
    //     zval *option;
    //     option = zend_hash_index_find(Z_ARRVAL_P(&MONITORPRO_G(curl_header)), Z_RES_HANDLE_P(zid));
    //     if (is_record)
    //     {
    //         if (option == nullptr)
    //         {
    //             option = (zval *)emalloc(sizeof(zval));
    //             bzero(option, sizeof(zval));
    //             array_init(option);
    //             is_emalloc = 1;
    //         }

    //         // for php7.3.0+
    // #if PHP_VERSION_ID >= 70300
    //         char *php_url_scheme = ZSTR_VAL(url_parse->scheme);
    //         char *php_url_host = ZSTR_VAL(url_parse->host);
    //         char *php_url_path = ZSTR_VAL(url_parse->path);
    //         char *php_url_query = ZSTR_VAL(url_parse->query);
    // #else
    //         char *php_url_scheme = url_parse->scheme;
    //         char *php_url_host = url_parse->host;
    //         char *php_url_path = url_parse->path;
    //         char *php_url_query = url_parse->query;
    // #endif
    //         std::string url_path = php_url_path == nullptr ? "/" : std::string(php_url_path);
    //         std::string url_query = php_url_query == nullptr ? "" : std::string(php_url_query);
    //         std::string url_host = php_url_host == nullptr ? "" : std::string(php_url_host);

    //         int peer_port;
    //         if (url_parse->port)
    //         {
    //             peer_port = url_parse->port;
    //         }
    //         else
    //         {
    //             if (strcasecmp("http", php_url_scheme) == 0)
    //             {
    //                 peer_port = 80;
    //             }
    //             else
    //             {
    //                 peer_port = 443;
    //             }
    //         }

    //         // SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
    //         // Segment *segment = skywalking_client->getSegment();
    //         // Span *span = segment->createSpan(SkySpanType::Exit, SkySpanLayer::Http, 8002);
    //         // span->setPeer(url_host + ":" + std::to_string(peer_port));
    //         // span->setOperationName(url_path);
    //         // span->addTag("url", url_str);

    //         // 拼装 pushgateway 数据
    //         long addr = reinterpret_cast<long>(zid);
    //         init_scaler_map(addr);
    //         // msc[addr]["second"]->addItem(ITEM_HTTP_FULL_PATH, url_path + "?" + url_query);
    //         msc[addr]["second"]->addItem(ITEM_HTTP_PATH, url_path);
    //         msc[addr]["second"]->addItem(ITEM_HTTP_PEER, url_host + ":" + std::to_string(peer_port));
    //         msc[addr]["second"]->setIndex(INDEX_HTTP_CLIENT_HANDLING_SECCONDS);
    //         // msc[addr]["total"]->addItem(ITEM_HTTP_FULL_PATH, url_path + "?" + url_query);
    //         msc[addr]["total"]->addItem(ITEM_HTTP_PATH, url_path);
    //         msc[addr]["total"]->addItem(ITEM_HTTP_PEER, url_host + ":" + std::to_string(peer_port));
    //         msc[addr]["total"]->setIndex(INDEX_HTTP_CLIENT_HANDLED_TOTAL);

    //         // std::string sw_header = segment->createHeader(span);
    //         // std::string sw_header_full = "sw8: " + sw_header;
    //         // add_next_index_string(option, sw_header_full.c_str());

    //         // zval argv[3];
    //         // zval ret;
    //         // ZVAL_STRING(&func, "curl_setopt");
    //         // ZVAL_COPY(&argv[0], zid);
    //         // ZVAL_LONG(&argv[1], CURLOPT_HTTPHEADER);
    //         // ZVAL_COPY(&argv[2], option);
    //         // call_user_function(CG(function_table), NULL, &func, &ret, 3, argv);

    //         // zval_dtor(&func);
    //         // zval_dtor(&ret);
    //         // zval_dtor(&argv[0]);
    //         // zval_dtor(&argv[1]);
    //         // zval_dtor(&argv[2]);
    //         if (is_emalloc)
    //         {
    //             zval_ptr_dtor(option);
    //             efree(option);
    //         }
    //     }

    //     zval_dtor(&url_info);
    //     if (url_parse != nullptr)
    //     {
    //         php_url_free(url_parse);
    //     }
}

void monitorpro_post_curl_exec_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    zval *zid;
    if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zid) == FAILURE)
    {
        return;
    }

    // record
    // if (is_record == 1)
    // {
    zval func;
    zval args[1];

    // SkywalkingClient *skywalking_client = SkywalkingClient::newInstance();
    // Segment *segment = skywalking_client->getSegment();
    // Span *span = segment->getLastSpan();

    // get response
    zval url_response;
    ZVAL_COPY(&args[0], zid);
    ZVAL_STRING(&func, "curl_getinfo");
    call_user_function(CG(function_table), NULL, &func, &url_response, 1, args);
    zval_dtor(&func);
    zval_dtor(&args[0]);

    // zval *response_http_code;
    // response_http_code = zend_hash_str_find(Z_ARRVAL(url_response), ZEND_STRL("http_code"));
    // span->addTag("status_code", std::to_string(Z_LVAL_P(response_http_code)));
    // if (Z_LVAL_P(response_http_code) >= 400)
    // {
    //     span->setIsError(true);
    // }
    // else
    // {
    //     span->setIsError(false);
    // }

    // span->setStartTime(start_micro_time / 1000);
    // span->setEndTime(end_micro_time / 1000);

    std::string url_path = "/";
    std::string url_query = "";
    std::string url_host = "";
    int peer_port = 80;
    php_url *url_parse = nullptr;
    zval *z_url = zend_hash_str_find(Z_ARRVAL(url_response), ZEND_STRL("url"));
    char *url_str = Z_STRVAL_P(z_url);
    if (strlen(url_str) > 0 && (starts_with("http://", url_str) || starts_with("https://", url_str)))
    {
        url_parse = php_url_parse(url_str);
        if (url_parse->scheme != nullptr && url_parse->host != nullptr)
        {
            // for php7.3.0+
#if PHP_VERSION_ID >= 70300
            char *php_url_scheme = ZSTR_VAL(url_parse->scheme);
            char *php_url_host = ZSTR_VAL(url_parse->host);
            char *php_url_path = ZSTR_VAL(url_parse->path);
            char *php_url_query = ZSTR_VAL(url_parse->query);
#else
            char *php_url_scheme = url_parse->scheme;
            char *php_url_host = url_parse->host;
            char *php_url_path = url_parse->path;
            char *php_url_query = url_parse->query;
#endif
            url_path = php_url_path == nullptr ? "/" : std::string(php_url_path);
            url_query = php_url_query == nullptr ? "" : std::string(php_url_query);
            url_host = php_url_host == nullptr ? "" : std::string(php_url_host);

            if (url_parse->port)
            {
                peer_port = url_parse->port;
            }
            else
            {
                if (strcasecmp("http", php_url_scheme) == 0)
                {
                    peer_port = 80;
                }
                else
                {
                    peer_port = 443;
                }
            }
        }
    }

    // 拼装 pushgateway 数据
    zval *z_http_code = zend_hash_str_find(Z_ARRVAL(url_response), ZEND_STRL("http_code"));
    zval *z_total_time = zend_hash_str_find(Z_ARRVAL(url_response), ZEND_STRL("total_time"));
    zval *z_primary_ip = zend_hash_str_find(Z_ARRVAL(url_response), ZEND_STRL("primary_ip"));
    zval *z_primary_port = zend_hash_str_find(Z_ARRVAL(url_response), ZEND_STRL("primary_port"));
    std::string http_code_str = z_http_code == nullptr ? "unknown" : std::to_string(Z_LVAL_P(z_http_code));
    std::string primary_ip_str = z_primary_ip == nullptr ? "unknown" : Z_STRVAL_P(z_primary_ip);
    std::string primary_port_str = z_primary_port == nullptr ? "unknown" : std::to_string(Z_LVAL_P(z_primary_port));

    if (MONITORPRO_G(pushenable))
    {
        long addr = reinterpret_cast<long>(zid);
        init_scaler_map(addr);
        msc[addr]["second"]->addItem(ITEM_HTTP_PATH, "");
        msc[addr]["second"]->addItem(ITEM_HTTP_FULL_PATH, url_path);
        msc[addr]["second"]->addItem(ITEM_HTTP_PEER, url_host + ":" + std::to_string(peer_port));
        msc[addr]["second"]->setIndex(INDEX_HTTP_CLIENT_HANDLING_SECCONDS);
        msc[addr]["total"]->addItem(ITEM_HTTP_PATH, "");
        msc[addr]["total"]->addItem(ITEM_HTTP_FULL_PATH, url_path);
        msc[addr]["total"]->addItem(ITEM_HTTP_PEER, url_host + ":" + std::to_string(peer_port));
        msc[addr]["total"]->setIndex(INDEX_HTTP_CLIENT_HANDLED_TOTAL);

        msc[addr]["second"]->addItem(ITEM_HTTP_ADDRESS, primary_ip_str + ":" + primary_port_str);
        msc[addr]["total"]->addItem(ITEM_HTTP_CODE, http_code_str);
        msc[addr]["total"]->addItem(ITEM_HTTP_ADDRESS, primary_ip_str + ":" + primary_port_str);

        PushGatewayClient *pushgateway_client = PushGatewayClient::newInstance();
        pushgateway_client->setCounter(msc[addr]["total"]);
        pushgateway_client->setBucket(msc[addr]["second"], Z_DVAL_P(z_total_time));

        delete msc[addr]["total"];
        delete msc[addr]["second"];
        msc[addr]["total"] = nullptr;
        msc[addr]["second"] = nullptr;
        msc.erase(addr);
    }

    zval_dtor(&url_response);
    if (url_parse != nullptr)
    {
        php_url_free(url_parse);
    }
    // }
}

void monitorpro_curl_close_handler(zend_execute_data *execute_data, zval *return_value, long start_micro_time, long end_micro_time, std::string class_name, std::string function_name)
{
    zval *zid;

    if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zid) == FAILURE)
    {
        return;
    }

    // zval *http_header = zend_hash_index_find(Z_ARRVAL_P(&MONITORPRO_G(curl_header)), Z_RES_HANDLE_P(zid));
    // if (http_header != NULL)
    // {
    //     zend_hash_index_del(Z_ARRVAL_P(&MONITORPRO_G(curl_header)), Z_RES_HANDLE_P(zid));
    // }

    if (MONITORPRO_G(pushenable))
    {
        long addr = reinterpret_cast<long>(zid);
        delete msc[addr]["total"];
        delete msc[addr]["second"];
        msc[addr]["total"] = nullptr;
        msc[addr]["second"] = nullptr;
        msc.erase(addr);
    }
}
